package nl.unimaas.micc.eventSnookerTest.simulation.event;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import nl.unimaas.micc.eventSnookerTest.domain.BilliardBall;
import nl.unimaas.micc.eventSnookerTest.domain.Ball.BallState;
import nl.unimaas.micc.eventSnookerTest.exec.Tester;
import nl.unimaas.micc.eventSnookerTest.math.Vector3D;
import nl.unimaas.micc.eventSnookerTest.simulation.physics.BallMotion;
import nl.unimaas.micc.eventSnookerTest.simulation.physics.Physics;


public class Interaction extends Event
{
	private Interaction(double time,Set<BilliardBall> balls)
	{
		super(time,balls);
	}
	
	public void handle()
	{
		BilliardBall[] b = new BilliardBall[2];
		b=getBalls().toArray(b);
		
		for(int i = 0; i<2; i++)
		{
			System.out.println("Ball ["+(i)+"] Was " + b[i].getState());			

			BallMotion motion = b[i].getState().getMotion();
			Vector3D av = motion.getAngularVelocity(b[i], getTime());
			Vector3D p = motion.getPosition(b[i], getTime());

			b[i].setTime(getTime());
			b[i].setAngularVelocity(av);
			b[i].setPosition(p);
			b[i].setState(BallState.SLIDING);
			System.out.println("Ball ["+(i)+"] Now " + b[i].getState());			
		}
		
		//
		// Elastic collision stuff
		//
		b[0].setVelocity(b[0].getState().getMotion().getVelocity(b[0], getTime()));
		b[1].setVelocity(b[1].getState().getMotion().getVelocity(b[1], getTime()));
		Vector3D unitNorm = b[1].getPosition().subtract(b[0].getPosition()).unit();
		//System.out.println("Unit Norm: " +unitNorm);
		Vector3D unitTangential = new Vector3D(- unitNorm.y, unitNorm.x, unitNorm.z);
		//System.out.println("Unit Tangential: " +unitTangential);
		double v1n = unitNorm.dotProduct(b[0].getVelocity());
		double v1t = unitTangential.dotProduct(b[0].getVelocity());
		
		//System.out.println("new V1n: " + v1n);
		//System.out.println("new V1t: " + v1t);
		
		double v2n = unitNorm.dotProduct(b[1].getVelocity());
		double v2t = unitTangential.dotProduct(b[1].getVelocity());

		// new v scalars
		//double newV1 = (v1n * (b[0].getMass()-b[1].getMass())+2*b[1].getMass()*v2n)/(b[0].getMass()+b[1].getMass());
		//double newV2 = (v2n * (b[1].getMass()-b[0].getMass())+2*b[0].getMass()*v1n)/(b[0].getMass()+b[1].getMass());
		
		//workaround: taking mass out since both masses are equal.
		
		double newV1 = v2n;
		double newV2 = v1n;
		

		//new vectors
		Vector3D newVector1n = unitNorm.multiply(newV1);
		Vector3D newVector1t = unitTangential.multiply(v1t);
		
		Vector3D newVector2n = unitNorm.multiply(newV2);
		Vector3D newVector2t = unitTangential.multiply(v2t);
		
		
		//set new V vectors
		b[0].setVelocity(newVector1n.add(newVector1t));
		b[1].setVelocity(newVector2n.add(newVector2t));
		
		Tester.markPoint(b[0].getState().getMotion().getPosition(b[0],
				this.getTime()).x, b[0].getState().getMotion().getPosition(
				b[0], this.getTime()).y, this.toString(), Color.RED);
	}

	public static Collection<Interaction> find(BilliardBall ball, Collection<BilliardBall> targets)
	{
		ArrayList<Interaction> ret = new ArrayList<Interaction>(targets.size());
		
		for(BilliardBall target : targets)
		{
			if(!target.equals(ball))
			{
				double maxTime = Math.max(ball.getTime(), target.getTime());

				BilliardBall advanced = null, initial = null, other = null;
				
				if (ball.getTime() < maxTime)
				{
					advanced = (BilliardBall) ball.clone();
					initial = ball;
					other = target;
				} else if (target.getTime() < maxTime)
				{
					advanced = (BilliardBall) target.clone();
					initial = target;
					other = ball;
				} else
				{
					advanced = ball;
					other = target;
				}
				
				if (advanced != null && initial != null)
				{					
					advanced.setPosition(advanced.getState().getMotion().getPosition(initial, maxTime));
					advanced.setVelocity(advanced.getState().getMotion().getVelocity(initial, maxTime));
					advanced.setAngularVelocity(advanced.getState().getMotion().getAngularVelocity(initial, maxTime));
				}
				
				double t = Physics.collisionTime(advanced, other);
				
				if(t > 0.00001)
				{
					HashSet<BilliardBall> h = new HashSet<BilliardBall>();
					h.add(ball);
					h.add(target);
					ret.add(new Interaction(maxTime+t,h));
				}
			}
			
		}
		return ret;
	}

}
